#include "sm_console.h"

#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "esp_vfs_fat.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h" 
#include "esp_err.h"

#define SM_CONSOLE_TAG "sm console"


#define SM_CONSOLE_TASK_STACK_SIZE    (4096)
static 	StaticTask_t 		sm_console_task_buffer;
static 	StackType_t 		sm_console_task_stack[SM_CONSOLE_TASK_STACK_SIZE] = {0};

static void initialize_console(void)
{
    /* Drain stdout before reconfiguring it */
    fflush(stdout);
    fsync(fileno(stdout));

    /* Disable buffering on stdin */
    setvbuf(stdin, NULL, _IONBF, 0);

    /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
    esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
    /* Move the caret to the beginning of the next line on '\n' */
    esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);

    // /* Configure UART. Note that REF_TICK is used so that the baud rate remains
    //  * correct while APB frequency is changing in light sleep mode.
    //  */
    // const uart_config_t uart_config = 
    // {
    //         .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
    //         .data_bits = UART_DATA_8_BITS,
    //         .parity = UART_PARITY_DISABLE,
    //         .stop_bits = UART_STOP_BITS_1,
    // #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
    //         .source_clk = UART_SCLK_REF_TICK,
    // #else
    //         .source_clk = UART_SCLK_XTAL,
    // #endif
    // };
    // /* Install UART driver for interrupt-driven reads and writes */
    // ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,256, 0, 0, NULL, 0));
    // ESP_ERROR_CHECK( uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));

    /* Tell VFS to use UART driver */
    esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);

    /* Initialize the console */
    esp_console_config_t console_config = 
    {
            .max_cmdline_args = 8,
            .max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
            .hint_color = atoi(LOG_COLOR_CYAN)
#endif
    };
    ESP_ERROR_CHECK( esp_console_init(&console_config) );

    /* Configure linenoise line completion library */
    /* Enable multiline editing. If not set, long commands will scroll within
     * single line.
     */
    linenoiseSetMultiLine(1);

    /* Tell linenoise where to get command completions and hints */
    linenoiseSetCompletionCallback(&esp_console_get_completion);
    linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);

    /* Set command history size */
    linenoiseHistorySetMaxLen(100);

    /* Don't return empty lines */
    // linenoiseAllowEmpty(true);

#if CONFIG_STORE_HISTORY
    /* Load command history from filesystem */
    linenoiseHistoryLoad(HISTORY_PATH);
#endif
}


static void sm_console_task(void *arg)
{
    /* Prompt to be printed before each line.
     * This can be customized, made dynamic, etc.
     */
    const char* prompt = LOG_COLOR_I CONFIG_IDF_TARGET "> " LOG_RESET_COLOR;

    /* Figure out if the terminal supports escape sequences */
    int probe_status = linenoiseProbe();
    if (probe_status) { /* zero indicates success */
        
        linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
        /* Since the terminal doesn't support escape sequences,
         * don't use color codes in the prompt.
         */
        // prompt = CONFIG_IDF_TARGET "> ";
#endif //CONFIG_LOG_COLORS
    }

    for(;;)
    {
        char* line = linenoise(prompt);
        if (line == NULL) { /* Break on EOF or error */
            break;
        }
        /* Add the command to the history if not empty*/
        if (strlen(line) > 0) {
            linenoiseHistoryAdd(line);
#if CONFIG_STORE_HISTORY
            /* Save command history to filesystem */
            linenoiseHistorySave(HISTORY_PATH);
#endif
        }

        /* Try to run the command */
        int ret;
        esp_err_t err = esp_console_run(line, &ret);
        if (err == ESP_ERR_NOT_FOUND) {
            printf("Unrecognized command\n");
        } else if (err == ESP_ERR_INVALID_ARG) {
            // command was empty
        } else if (err == ESP_OK && ret != ESP_OK) {
            printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
        } else if (err != ESP_OK) {
            printf("Internal error: %s\n", esp_err_to_name(err));
        }
        /* linenoise allocates line buffer on the heap, so need to free it */
        linenoiseFree(line);

        vTaskDelay(50 / portTICK_RATE_MS);
    }

    ESP_LOGE(SM_CONSOLE_TAG, "Error or end-of-input, terminating console");
    esp_console_deinit();
}

inline esp_err_t sm_console_add_table(const esp_console_cmd_t *cmd)
{
    return esp_console_cmd_register(cmd);
}

static int reset_system_cmd_func(int arge,char** argv)
{
    if(arge == 1)
    {
        ESP_LOGW(SM_CONSOLE_TAG,"reset system,delay 3s");
        vTaskDelay(3000 / portTICK_PERIOD_MS);
        esp_restart();
    }
    else
    {
        return 1;
    }
    return 0;
}

void sm_console_init(void)
{
    initialize_console();

	esp_console_register_help_command();

    static esp_console_cmd_t reset_system_cmd = 
    {
        .command = "reboot",
        .help = "reset system cmd",
        .func = &reset_system_cmd_func
    };

    sm_console_add_table(&reset_system_cmd);

    xTaskCreateStatic(
					(TaskFunction_t   ) sm_console_task,
					(const char*      ) "SmConsoleTask",     /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
					(uint32_t         ) SM_CONSOLE_TASK_STACK_SIZE,
					(void*            ) NULL,
					(UBaseType_t      ) tskIDLE_PRIORITY+1,
					(StackType_t*     ) sm_console_task_stack,
					(StaticTask_t*    ) &sm_console_task_buffer
					);

}





